/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/js_arraybuffer.h"
#include "plugins/ecmascript/runtime/js_dataview.h"
#include "plugins/ecmascript/runtime/object_factory.h"
#include "plugins/ecmascript/tests/runtime/common/test_helper.h"

// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript;

namespace ark::test {
class JSDataViewTest : public testing::Test {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "SetUpTestCase";
    }

    static void TearDownTestCase()
    {
        GTEST_LOG_(INFO) << "TearDownCase";
    }

    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_);
    }

    void TearDown() override
    {
        TestHelper::DestroyEcmaVMWithScope(instance_, scope_);
    }

protected:
    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
    JSThread *thread_ {nullptr};

private:
    PandaVM *instance_ {nullptr};
    ecmascript::EcmaHandleScope *scope_ {nullptr};
};

/*
 * Feature: JSDataView
 * Function: GetElementSize
 * SubFunction: N/A
 * FunctionPoints: Get ElementSize
 * CaseDescription: Check whether the returned value through "GetElementSize" function is within expectations.
 */
TEST_F(JSDataViewTest, GetElementSize)
{
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT8), 1);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT8), 1);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT8_CLAMPED), 1);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT16), 2);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT16), 2);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT32), 4);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT32), 4);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::FLOAT32), 4);
    EXPECT_EQ(JSDataView::GetElementSize(DataViewType::FLOAT64), 8);
}

/*
 * Feature: JSDataView
 * Function: SetDataView
 * SubFunction: GetDataView
 * FunctionPoints: Set DataView
 * CaseDescription: Check whether the returned value through "GetDataView" function is within expectations after
 *                  calling "SetDataView" function.
 */
TEST_F(JSDataViewTest, SetDataView)
{
    EcmaVM *ecmaVmPtr = thread_->GetEcmaVM();
    ObjectFactory *factory = ecmaVmPtr->GetFactory();
    JSHandle<GlobalEnv> handleGlobalEnv = ecmaVmPtr->GetGlobalEnv();

    int32_t lengthDataArrayBuf = 8;
    int32_t offsetDataView = 4;
    int32_t lengthDataView = 4;
    JSHandle<JSFunction> handleFuncArrayBuf(handleGlobalEnv->GetArrayBufferFunction());
    JSHandle<JSTaggedValue> handleTagValFuncArrayBuf(handleFuncArrayBuf);
    JSHandle<JSArrayBuffer> handleArrayBuf(
        factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf));
    handleArrayBuf->SetArrayBufferByteLength(thread_, JSTaggedValue(lengthDataArrayBuf));

    // Call "SetDataView" function through "NewJSDataView" function of "object_factory.cpp"
    JSHandle<JSDataView> handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView, lengthDataView);
    EXPECT_TRUE(handleDataView->GetDataView().IsTrue());

    // Call "SetDataView" function in this TEST_F.
    handleDataView->SetDataView(thread_, JSTaggedValue::False());
    EXPECT_TRUE(handleDataView->GetDataView().IsFalse());
    handleDataView->SetDataView(thread_, JSTaggedValue::True());
    EXPECT_TRUE(handleDataView->GetDataView().IsTrue());
}

/*
 * Feature: JSDataView
 * Function: SetViewedArrayBuffer
 * SubFunction: GetViewedArrayBuffer
 * FunctionPoints: Set ViewedArrayBuffer
 * CaseDescription: Check whether the returned value through "GetViewedArrayBuffer" function is within expectations
 *                  after calling "SetViewedArrayBuffer" function.
 */
TEST_F(JSDataViewTest, SetViewedArrayBuffer)
{
    EcmaVM *ecmaVmPtr = thread_->GetEcmaVM();
    ObjectFactory *factory = ecmaVmPtr->GetFactory();
    JSHandle<JSFunction> handleFuncArrayBuf(ecmaVmPtr->GetGlobalEnv()->GetArrayBufferFunction());
    JSHandle<JSTaggedValue> handleTagValFuncArrayBuf(handleFuncArrayBuf);

    int32_t lengthDataArrayBuf1 = 8;
    // NOLINTNEXTLINE(readability-magic-numbers)
    int32_t lengthDataArrayBuf2 = 16;
    int32_t offsetDataView = 4;
    int32_t lengthDataView = 4;
    JSHandle<JSArrayBuffer> handleArrayBuf1(
        factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf));
    JSHandle<JSArrayBuffer> handleArrayBuf2(
        factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf));
    handleArrayBuf1->SetArrayBufferByteLength(thread_, JSTaggedValue(lengthDataArrayBuf1));
    handleArrayBuf2->SetArrayBufferByteLength(thread_, JSTaggedValue(lengthDataArrayBuf2));

    // Call "SetViewedArrayBuffer" function through "NewJSDataView" function of "object_factory.cpp"
    JSHandle<JSDataView> handleDataView = factory->NewJSDataView(handleArrayBuf1, offsetDataView, lengthDataView);
    JSHandle<JSTaggedValue> handleTagValDataViewFrom1(thread_, handleArrayBuf1.GetTaggedValue());
    JSHandle<JSTaggedValue> handleTagValDataViewTo1(thread_, handleDataView->GetViewedArrayBuffer());
    EXPECT_TRUE(JSTaggedValue::Equal(thread_, handleTagValDataViewFrom1, handleTagValDataViewTo1));

    // Call "SetViewedArrayBuffer" function in this TEST_F.
    handleDataView->SetViewedArrayBuffer(thread_, handleArrayBuf2.GetTaggedValue());
    JSHandle<JSTaggedValue> handleTagValDataViewFrom2(thread_, handleArrayBuf2.GetTaggedValue());
    JSHandle<JSTaggedValue> handleTagValDataViewTo2(thread_, handleDataView->GetViewedArrayBuffer());
    EXPECT_TRUE(JSTaggedValue::Equal(thread_, handleTagValDataViewFrom2, handleTagValDataViewTo2));
    EXPECT_FALSE(JSTaggedValue::Equal(thread_, handleTagValDataViewFrom1, handleTagValDataViewFrom2));
}

/*
 * Feature: JSDataView
 * Function: SetByteLength
 * SubFunction: GetByteLength
 * FunctionPoints: Set ByteLength
 * CaseDescription: Check whether the returned value through "GetByteLength" function is within expectations after
 *                  calling "SetByteLength" function.
 */
TEST_F(JSDataViewTest, SetByteLength)
{
    EcmaVM *ecmaVmPtr = thread_->GetEcmaVM();
    ObjectFactory *factory = ecmaVmPtr->GetFactory();
    JSHandle<JSFunction> handleFuncArrayBuf(ecmaVmPtr->GetGlobalEnv()->GetArrayBufferFunction());
    JSHandle<JSTaggedValue> handleTagValFuncArrayBuf(handleFuncArrayBuf);

    int32_t lengthDataArrayBuf = 8;
    int32_t offsetDataView = 4;
    int32_t lengthDataView1 = 4;
    int32_t lengthDataView2 = 2;
    JSHandle<JSTaggedValue> handleTagValLengthDataView2(thread_, JSTaggedValue(lengthDataView2));
    JSHandle<JSArrayBuffer> handleArrayBuf(
        factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf));
    handleArrayBuf->SetArrayBufferByteLength(thread_, JSTaggedValue(lengthDataArrayBuf));

    // Call "SetByteLength" function through "NewJSDataView" function of "object_factory.cpp"
    JSHandle<JSDataView> handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView, lengthDataView1);
    EXPECT_EQ(handleDataView->GetByteLength().GetNumber(), lengthDataView1);

    // Call "SetByteLength" function in this TEST_F.
    handleDataView->SetByteLength(thread_, handleTagValLengthDataView2);
    EXPECT_EQ(handleDataView->GetByteLength().GetNumber(), lengthDataView2);
}

/*
 * Feature: JSDataView
 * Function: SetByteOffset
 * SubFunction: GetByteOffset
 * FunctionPoints: Set ByteOffset
 * CaseDescription: Check whether the returned value through "GetByteOffset" function is within expectations after
 *                  calling "SetByteOffset" function.
 */
TEST_F(JSDataViewTest, SetByteOffset)
{
    EcmaVM *ecmaVmPtr = thread_->GetEcmaVM();
    ObjectFactory *factory = ecmaVmPtr->GetFactory();
    JSHandle<JSFunction> handleFuncArrayBuf1(ecmaVmPtr->GetGlobalEnv()->GetArrayBufferFunction());
    JSHandle<JSTaggedValue> handleTagValFuncArrayBuf1(handleFuncArrayBuf1);

    int32_t lengthDataArrayBuf = 8;
    int32_t offsetDataView1 = 4;
    int32_t offsetDataView2 = 6;
    int32_t lengthDataView = 2;
    JSHandle<JSTaggedValue> handleTagValOffsetDataView2(thread_, JSTaggedValue(offsetDataView2));
    JSHandle<JSArrayBuffer> handleArrayBuf(
        factory->NewJSObjectByConstructor(handleFuncArrayBuf1, handleTagValFuncArrayBuf1));
    handleArrayBuf->SetArrayBufferByteLength(thread_, JSTaggedValue(lengthDataArrayBuf));

    // Call "SetByteOffset" function through "NewJSDataView" function of "object_factory.cpp"
    JSHandle<JSDataView> handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView1, lengthDataView);
    EXPECT_EQ(handleDataView->GetByteOffset().GetNumber(), offsetDataView1);

    // Call "SetByteOffset" function in this TEST_F.
    handleDataView->SetByteOffset(thread_, handleTagValOffsetDataView2);
    EXPECT_EQ(handleDataView->GetByteOffset().GetNumber(), offsetDataView2);
}
}  // namespace ark::test
